package at.ac.tuwien.swa.sparrow

import at.ac.tuwien.swa.sparrow.desktop.{SWAzam, SWAzamRuntime}
import at.ac.tuwien.swa.sparrow.peer.PeerActor
import at.ac.tuwien.swa.sparrow.peer.component.{AudioFingerprintFunction, RequestHandler, IndexingWorker, MusicLibrary}
import at.ac.tuwien.swa.sparrow.server.component.{UserManager, TicketProvider, PeerManager, BillingEngine}
import at.ac.tuwien.swa.sparrow.server.controller.{ClientController, PeerController}
import at.ac.tuwien.swa.sparrow.server.{PeerService, ClientService}
import at.ac.tuwien.swa.sparrow.web.{WebController, AdminController}
import java.lang.reflect.{Modifier, Field}
import org.springframework.core.io.ClassPathResource
import org.springframework.stereotype.Repository
import org.springframework.util.ReflectionUtils._
import scala.Some
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.xml.XML

object ModelChecker extends App {

	type NamedPath = (String, String)
	type TypedPath = (Class[_], Class[_])

	val mapping = new mutable.HashMap[String, mutable.Set[Class[_]]] with mutable.MultiMap[String, Class[_]] {
		addBinding("Billing Engine", classOf[BillingEngine])
		addBinding("Client", classOf[SWAzamRuntime])
		addBinding("Database", classOf[MusicLibrary])
		addBinding("Hash Function", classOf[AudioFingerprintFunction])
		addBinding("Hash Function", classOf[desktop.AudioFingerprintFunction])
		addBinding("Indexing Worker", classOf[IndexingWorker])
		addBinding("Peer", classOf[PeerActor])
		addBinding("Peer Management", classOf[PeerService])
		addBinding("Peer Management", classOf[PeerController])
		addBinding("Peer Management", classOf[PeerManager])
		addBinding("Recorder", classOf[SWAzamRuntime])
		addBinding("Request Handler", classOf[PeerActor])
		addBinding("Request Handler", classOf[RequestHandler])
		addBinding("Server", classOf[ClientService])
		addBinding("Server", classOf[PeerService])
		addBinding("Ticket Provider", classOf[TicketProvider])
		addBinding("UI", classOf[SWAzam])
		addBinding("UI", classOf[SWAzamRuntime])
		addBinding("UI", classOf[AdminController])
		addBinding("UI", classOf[WebController])
		addBinding("User Management", classOf[ClientController])
		addBinding("User Management", classOf[ClientService])
		addBinding("User Management", classOf[UserManager])
	}

	val root = XML loadFile new ClassPathResource("swazam_out.xml").getFile
	val paths = (root \ "archStructure" filterNot (_ \ "@name" exists (_.text == "SWAzam"))) \\ "path"
	val specPaths = (paths map (p => ((p \ "src" \ "@name").text, (p \ "dest" \ "@name").text))).toSet

	for (specPath <- specPaths.toList.sorted) {
		val srcComps = mapping(specPath._1)
		val destComps = mapping(specPath._2)
		val tupples = srcComps flatMap (s => destComps map ((s, _)))
		tupples find {case (s, d) => findField(s, null, d) != null} match {
			case None => connector(specPath)
			case Some(c: TypedPath) => connector(specPath, c)
		}
	}

	println("#" * 120)

	val componentFields = for {
		srcComp <- mapping.values.flatten.toSet[Class[_]]
		f <- SwaComponentFilter(srcComp)
	} yield (srcComp, f.getType)

	for (cf <- componentFields.toList.sortBy(_._1.getSimpleName)) {
		val srcNames = mapping.filter(_._2 contains cf._1).keySet
		val destNames = mapping.filter(_._2 contains cf._2).keySet
		val paths = srcNames flatMap (s => destNames map ((s, _)))
		val candidates = specPaths intersect paths
		candidates foreach (connector(_, cf))
		if (candidates.isEmpty)	{
			paths.find(p => p._1 == p._2) match {
				case None => connector(("", ""), cf)
				case Some(p: NamedPath) => connector(p, cf)
			}
		}
	}

	def connector(path: NamedPath) {
		println("%-20s -> %-20s: ".format(path._1, path._2))
	}

	def connector(path: NamedPath, comps: TypedPath) {
		println("%-20s -> %-20s: %-25s -> %-25s".format(path._1, path._2, comps._1.getSimpleName, comps._2.getSimpleName))
	}
}

object SwaComponentFilter {
	def apply(clazz: Class[_]) = {
		val filter = new SwaComponentFilter()
		doWithFields(clazz, filter, filter)
		filter.fields.toList
	}
}

class SwaComponentFilter private() extends FieldFilter with FieldCallback {
	val fields = new ListBuffer[Field]()

	def matches(field: Field) = (field.getType.getName startsWith "at.ac.tuwien.swa") &&
		!(field.getType isAnnotationPresent classOf[Repository]) && !(Modifier isStatic field.getModifiers)

	def doWith(field: Field) = fields += field
}
